﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace XCommunity.Caching
{
  /// <summary>
  /// 缓存容器，提供缓存项的检索和管理
  /// </summary>
  public class CacheContainer
  {


    private CacheService _cacheService;
    private Hashtable _dictionary;

    protected IDictionary Items
    {
      get { return _dictionary; }
    }

    /// <summary>
    /// 初始化缓存对象
    /// </summary>
    /// <param name="cacheService"></param>
    public virtual void Init( CacheService cacheService )
    {
      _cacheService = cacheService;
      _dictionary = new Hashtable();

      _cacheService.CleanCache += OnCleanCache;
    }


    /// <summary>
    /// 所依赖的缓存服务
    /// </summary>
    public CacheService Service
    {
      get { return _cacheService; }
    }



    /// <summary>
    /// 添加一个缓存项
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    protected CacheItem AddItem( object key, object value )
    {
      CacheItem item = CreateCacheItem( key, value );
      AddItem( item );
      return item;
    }

    /// <summary>
    /// 添加一个缓存项
    /// </summary>
    /// <param name="cacheItem"></param>
    protected void AddItem( CacheItem cacheItem )
    {
      CacheItem existItem = TryGetItem( cacheItem.Key );
      if ( existItem != null )
      {
        Service.RemoveCacheItem( existItem );
      }


      Service.AddCacheItem( cacheItem, this );

      lock ( Items.SyncRoot )
      {
        Items.Remove( cacheItem.Key );
        Items.Add( cacheItem.Key, cacheItem );
      }
    }


    /// <summary>
    /// 创建一个缓存项，派生类重写此方法以创建自己的缓存项
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    protected virtual CacheItem CreateCacheItem( object key, object value )
    {
      return new CacheItem( key, value );
    }


    /// <summary>
    /// 尝试获取一个缓存项
    /// </summary>
    /// <param name="key">索引键</param>
    /// <returns></returns>
    protected CacheItem TryGetItem( object key )
    {
      return (CacheItem) Items[key];
    }

    /// <summary>
    /// 尝试获取有效的缓存项，若尝试获取的缓存项已失效，则返回null
    /// </summary>
    /// <param name="key">索引键</param>
    /// <returns></returns>
    protected CacheItem TryGetAvailableItem( object key )
    {
      CacheItem item = TryGetItem( key );

      if ( item == null )
        return null;

      if ( item.Available )
        return item;
      else
        return null;
    }

    /// <summary>
    /// 尝试从缓存中读取一个值，如果失败或者该项已过期，则返回null
    /// </summary>
    /// <param name="key">索引键</param>
    /// <returns></returns>
    protected object TryGetValue( object key )
    {
      CacheItem item = (CacheItem) Items[key];
      if ( item == null )
        return null;
      else
        return item.TryGetValue();
    }

    /// <summary>
    /// 强行移除一个缓存项
    /// </summary>
    /// <param name="key">索引键</param>
    /// <returns></returns>
    protected CacheItem RemoveCacheItem( object key )
    {
      CacheItem cacheItem = TryGetItem( key );

      if ( cacheItem == null )
        return null;

      Service.RemoveCacheItem( cacheItem );
      Items.Remove( key );

      return cacheItem;
    }


    public int Count
    {
      get { return Items.Count; }
    }


    /// <summary>
    /// 当缓存服务通知清理缓存时发生
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected virtual void OnCleanCache( object sender, EventArgs e )
    {

      ArrayList keyList = new ArrayList();

      foreach ( DictionaryEntry entry in _dictionary )
      {
        if ( !( (CacheItem) entry.Value ).Available )
          keyList.Add( entry.Key );
      }


      foreach ( object key in keyList )
      {
        _dictionary.Remove( key );
      }
    }


    private long spanTicks = new TimeSpan( 0, 2, 0 ).Ticks;

    /// <summary>
    /// 通知检查缓存项是否仍然有效
    /// </summary>
    /// <param name="cacheItem"></param>
    /// <returns></returns>
    public virtual bool CheckAvailable( CacheItem cacheItem )
    {
      return Service.GetTicks() <= cacheItem.RefreshTime.Ticks + spanTicks;
    }




    /// <summary>
    /// 从缓存中获取指定缓存值，若该值未被缓存则加载到缓存。
    /// </summary>
    /// <typeparam name="TValue">值类型</typeparam>
    /// <typeparam name="TKey">索引键类型</typeparam>
    /// <param name="key">索引键</param>
    /// <param name="loader">值加载器</param>
    /// <returns>值</returns>
    protected TValue GetOrLoadValue<TKey, TValue>( TKey key, Func<TKey, TValue> loader )
    {
      TValue value = (TValue) TryGetValue( key );

      if ( value == null )
      {
        value = LoadValue( key, loader );
      }

      return value;
    }


    /// <summary>
    /// 直接加载指定值到缓存，无论该值是否存在于缓存中。
    /// </summary>
    /// <typeparam name="TValue">值类型</typeparam>
    /// <typeparam name="TKey">索引键类型</typeparam>
    /// <param name="key">索引键</param>
    /// <param name="loader">值加载器</param>
    /// <returns>值</returns>
    private TValue LoadValue<TKey, TValue>( TKey key, Func<TKey, TValue> loader )
    {
      TValue value = loader( key );

      if ( value == null )
        return default( TValue );

      AddItem( key, value );

      return value;
    }


  }


  /// <summary>
  /// 对CacheContainer的一个强类型标准实现
  /// </summary>
  /// <typeparam name="TKey"></typeparam>
  /// <typeparam name="TValue"></typeparam>
  public class CacheContainer<TKey, TValue> : CacheContainer
  {

    public void Add( TKey key, TValue value )
    {
      base.AddItem( key, value );
    }

    public bool TryGetValue( TKey key, out TValue value )
    {

      object _value = base.TryGetValue( key );
      if ( _value == null )
      {
        value = default( TValue );
        return false;
      }

      value = (TValue) _value;
      return true;
    }

    public TValue GetOrLoadValue( TKey key, Func<TKey, TValue> loader )
    {
      return base.GetOrLoadValue( key, loader );
    }

  }

}
